// SampleServer.cpp
//
// by Brian Schick
// 5/17/2010
//
// For Week 4, GSP460
// To practice TCP sockets, multithreading, synchronization


// Input: a client process connects to and sends a message to the server
// Output: on a successful receive, 
//	prints the received message to the screen for each message received
// Sample Output:
/*
Hello World
desc: WinSock 2.0
desc: Running
0x202
Winsock2 started...
1 recv: Client says 'Hello!'
and send 21 bytes.
Time to close.
1 recv: Client says 'Hello!'
and send 21 bytes.
Time to close.
1 recv: Client says 'Hello!'
and send 21 bytes.
Time to close.
SampleServer is done.
Press any key to continue . . .//
*/


// TCP networking - A non-blocking server
//	using Winsock2.2
//
	// A game loop for networking
	// Init
	//	Init Winsock
	//	Init socket
	//		Build socket
	//		Bind socket
	//		Listen <- What does this do?
	// Loop
	//	Msg
	//		accept until get message
	//		_beginthread a recv/send thread
	//		quit loop on 'Q' press.  // not working with blocking socket
	// Exit


// Notes:
// TODO:
//		To assure that all data is sent and received on a connected socket before it is closed, an application should use shutdown to close connection before calling closesocket. For example, to initiate a graceful disconnect:
//		Call WSAAsyncSelect to register for FD_CLOSE notification. 
//		Call shutdown with how=SD_SEND. 
//		When FD_CLOSE received, call recv until zero returned, or SOCKET_ERROR. 
//		Call closesocket. 
//	Note  The shutdown function does not block regardless of the SO_LINGER setting on the socket.

// SELECT
//	compile with USE_SELECT defined to use select to avoid a blocking socket
//  compile with USE_SELECT undefined to use non-blocking socket

#pragma once

#include "WSAErrors.h"
#include <iostream>
#include <iomanip>
#include <Winsock2.h>
#include <Windows.h>
#include <process.h>

#define INPUTBUFSIZE	1024
#define SRVR_IP			"127.0.0.1"
#define SRVR_PORT		7654
#define MSG_BUFFER_SIZE	512				// for the message size
#define INBUFSIZE		128				// for the keyboard input

// Critical section syncs
CRITICAL_SECTION g_bufferSection;

using namespace std;

// talkthreadstuff
//	structure to hold parameters shared by threads
//	- talkSocket: the new thread's socket for talking with that particular client
//	- runMutex: the Mutex held when the main thread runs
//	- outputMutex: the Mutex held to use cout
//	- outputHandle: a handle to STDOUT
//	- bufferMutex: the Mutex held to r/w to the shared char buffer
//	- numThreads: the current number of Talk threads active. (LONGLONG for interlockedadd64)
struct talkthreadstuff
{
	SOCKET talkSocket;	// active connection 
	HANDLE runMutex;	// shared mutex
	HANDLE outputMutex;	// not used
	HANDLE outputHandle;// not used
	HANDLE bufferMutex; // not used
	char * msgBuff;		// shared msg buffer
	LONGLONG numThreads;// shared active thread counter
};

// Calls WSACleanUp and shows errors
void CleanWSA();

// Calls shutdown( s, SD_BOTH ), closesocket( s ), and shows errors
void ShutAndCleanSocket( SOCKET s );

// Calls ReleaseMutex and shows msg on error
void MutexRelease( HANDLE m );

// TalkOnTheSocket
//	The routine for the talking threads.
//	Receives and sends back a message from a client
//	then shuts down gracefully
void TalkOnTheSocket( talkthreadstuff * masterSocket );

// Increments number of threads in the passed param structure
//	Uses the interlockedIncrement64 call to sync with other threads
void IncrementThreads( talkthreadstuff * a_threadstuff );

// Decrements number of threads in the passed param structure
//	Uses the interlockedDecrement64 call to sync with other threads
void DecrementThreads( talkthreadstuff * a_threadstuff );

// Counts number of threads in the passed param structure
//	Uses the interlockedOr64 call to sync with other threads
LONGLONG CountThreads( talkthreadstuff * a_threadstuff );

// Sets up a socket to accept connections attempts
//	and spawns a thread to receive and echo a message.
int main( int argc, char * argv[] )
{
	cout << "BriansSampleServer" << endl;
	long errcode = 0;

	//	Need control syncs-
	talkthreadstuff theStuff;

	// Only want one SampleServer running (on this IP and port)
	theStuff.runMutex = NULL;
	theStuff.runMutex = CreateMutex( NULL, true, "BriansSampleServerRunMutex" );
	errcode = GetLastError();
	if ( ERROR_ALREADY_EXISTS == errcode )
	{
		cout << "Already Running" << endl;
		return 1;
	}

	cout << "Hello World" << endl;

	// A game loop for networking
	// Init
	//	Init Winsock

	// Need a WSAData <- What's that?
	//	WSAData - a structure holding Winsock info.
	WSAData myWSAInfo;
	memset(&myWSAInfo, 0, sizeof(WSAData) ); // Initialize the struct to all 0s.
	// 0x0202: Ask for Winsock2.2: minor version 2 (high byte) of major version 2 (low byte)
	//	It doesn't seem to matter which version I ask for.  I always get ver 2.2?
	//	It does matter if it gets a good WSAData pointer.
	errcode = WSAStartup( 0x0202, &myWSAInfo ); 
	// Did it work?
	WSAErrorsToConsole::ShowWSAStartupError( errcode );
	// Lets see what I get
	cout << "desc: " << myWSAInfo.szDescription << endl; // Always shows Winsock ver 2.0.  Why?
	cout << "desc: " << myWSAInfo.szSystemStatus << endl;
	cout << setbase(16) << showbase << myWSAInfo.wHighVersion << setbase(10) << endl; // It IS 2.2!
	// Enough!  Did it work?
	if ( 0 != errcode )
	{
		CleanWSA();
		MutexRelease( theStuff.runMutex );
		CloseHandle( theStuff.runMutex );
		return 1;
	}
	cout << "Winsock2 started..." << endl;

	//	Init socket
	//		Build socket
	SOCKET myServerSocket = INVALID_SOCKET;
	myServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP );
	if ( INVALID_SOCKET == myServerSocket )
	{
		WSAErrorsToConsole::ShowSocketError( WSAGetLastError() );
		ShutAndCleanSocket( myServerSocket );
		CleanWSA();
		MutexRelease( theStuff.runMutex );
		CloseHandle( theStuff.runMutex );
		return 1;
	}

#ifndef USE_SELECT
	// Change listening socket to non-blocking
	// ioctl and ioctlsocket way to non-blocking sockets
	u_long non_blocking = true; // true = anyval but 0; false = 0
	ioctlsocket( myServerSocket, FIONBIO, &non_blocking );
#endif

	//		Bind socket
	//			Need an address
	sockaddr_in myServerAddress;
	myServerAddress.sin_family = AF_INET;
	myServerAddress.sin_addr.s_addr = INADDR_NONE;
	myServerAddress.sin_addr.s_addr = inet_addr(SRVR_IP);
	if ( INADDR_NONE == myServerAddress.sin_addr.s_addr )
	{
		cout << "Improper IP address: " << SRVR_IP << endl;

		ShutAndCleanSocket( myServerSocket );
		CleanWSA();
		MutexRelease( theStuff.runMutex );
		CloseHandle( theStuff.runMutex );
		return 1;
	}
	myServerAddress.sin_port = htons(SRVR_PORT);
	//			Then bind
	errcode = bind( myServerSocket, 
			(SOCKADDR*)&myServerAddress, 
			sizeof(sockaddr_in) );
	if ( SOCKET_ERROR == errcode )
	{
		WSAErrorsToConsole::ShowBindError( errcode );
		ShutAndCleanSocket( myServerSocket );
		CleanWSA();
		MutexRelease( theStuff.runMutex );
		CloseHandle( theStuff.runMutex );
		return 1;
	}

	//		Listen <- ?
	errcode = listen( myServerSocket, SOMAXCONN );
	if ( SOCKET_ERROR == errcode )
	{
		WSAErrorsToConsole::ShowListenError( errcode );
		ShutAndCleanSocket( myServerSocket );
		CleanWSA();
		MutexRelease( theStuff.runMutex );
		CloseHandle( theStuff.runMutex );
		return 1;
	}

	// put accept and talk thread spawn in a loop
	//	Need a client socket
	SOCKET acceptSocket = INVALID_SOCKET;
	//	Need a client address
	sockaddr_in acceptAddress;
	int clientAddrSize = sizeof( sockaddr_in );

	// Need a shared message buffer to exercise the syncs
	char sharedBuff[MSG_BUFFER_SIZE];
	memset( sharedBuff, '\0', MSG_BUFFER_SIZE * sizeof( char ) );

	// Initialize remaining control syncs 
	theStuff.talkSocket = INVALID_SOCKET;
	theStuff.outputHandle = GetStdHandle( STD_OUTPUT_HANDLE );
	theStuff.outputMutex = NULL;
	theStuff.bufferMutex = NULL;
	theStuff.msgBuff = sharedBuff;
	theStuff.numThreads = 0;
	//	Initialize syncs
	InitializeCriticalSection(&g_bufferSection);

	// Loop until user presses a 'q'.  But, don't want to block on keyboard input
	HANDLE stdinHandle = GetStdHandle( STD_INPUT_HANDLE );
	bool runserver = true;
	DWORD isInput;
	INPUT_RECORD ir[INBUFSIZE];

	SetConsoleMode( stdinHandle, ENABLE_WINDOW_INPUT );

	//select // see for non-blocking sockets // used with USE_SELECT defined.
	fd_set readsocks, writesocks, errsocks; // array of 64 sockets, and one counter
	timeval timeout;
	timeout.tv_sec = 0;		//secs
	timeout.tv_usec = 250000;	//microsecs ( < 4 checks a second )

	while ( runserver )
	{	
		// Check for a 'q' to end server.
		//	Useless with a blocking accept!
		isInput = 0;
		PeekConsoleInput( stdinHandle, ir, INBUFSIZE, &isInput );
		if ( isInput > 0 )
		{
			ReadConsoleInput( stdinHandle, ir, INBUFSIZE, &isInput );
			for ( unsigned int i = 0; i < isInput; i++ )
			{
				switch( ir[i].EventType )
				{
				case KEY_EVENT:
					if ( ir[i].Event.KeyEvent.bKeyDown )
					{
						switch( ir[i].Event.KeyEvent.wVirtualKeyCode )
						{
						case 'Q':
						case 'q':
							runserver = false;
							break;
						case VK_SPACE:
							break;
						}
					}
					break;
				}
			}
		}
		else
		{
			// accept until get message from ONE client.
			//	will change to skip accept unless select shows ready to accept

#ifdef USE_SELECT
		// Change loop to logic off of select
		// Clear my socket(handle) lists
		FD_ZERO( &readsocks );  FD_ZERO( &writesocks );  FD_ZERO( &errsocks );
		// Insert my sockets(handles) of interest
		//	Notes: Can't get select to operate on stdin.
		//	- trying to get a stdin fd.
		//FD_SET( _open_osfhandle( (long)stdinHandle,O_RDONLY), &readsocks ); // not a socket
		//FD_SET( _fileno( stdin ), &readsocks );                             // not a socket
		FD_SET( myServerSocket, &readsocks );                               // works
		// Check sockets(handles) of interest
		errcode = select( 0, &readsocks, &writesocks, &errsocks, &timeout );
		if ( errcode == SOCKET_ERROR )// an error
		{
			WSAErrorsToConsole::ShowWinsock2Error( WSAGetLastError() );
		}
		else if ( errcode == 0 )
		{
			cout << "sel0 ";
		}
		else
		{
			cout << "selwk ";
#endif


			// accept until get message from ONE client.
			//	Note: put in loop to run multiple times to get multiple connections from one or more clients.
			//		call accept to wait for a connect from a client
			//	Move to a loop to accept multiple connects from multiple clients,
			//		and spawn a new thread to talk over the connection.
			//	Try to get a non-blocking accept. // 5/18/10 - made one using ioctlsocket
			acceptSocket = INVALID_SOCKET;
			acceptSocket = accept( myServerSocket, 
				(SOCKADDR*)&acceptAddress, 
				&clientAddrSize );
			if ( INVALID_SOCKET == acceptSocket )
			{
				errcode = WSAGetLastError();
				if ( WSAEWOULDBLOCK != errcode )
				{
				// may not close child threads ( or attatched clients ) gracefully
				WSAErrorsToConsole::ShowAcceptError( errcode );
				ShutAndCleanSocket( acceptSocket );
				ShutAndCleanSocket( myServerSocket );
				CleanWSA();
				MutexRelease( theStuff.runMutex );
				CloseHandle( theStuff.runMutex );
				return 1;
				}
			}

			if ( INVALID_SOCKET != acceptSocket )
			{
				// Increment number of threads
				IncrementThreads( &theStuff );

				theStuff.talkSocket = acceptSocket;
				// Need to move into a new thread to recv and send and close the 
				_beginthread( (void(__cdecl*)(void*))TalkOnTheSocket, 0, &theStuff );
			}
#ifdef USE_SELECT
		}
#endif
		}
	}

	// Wait for all the TalkOnTheSocket threads to finish
	//	then release both critical sections
	int countcopy = 0;
	do 
	{	
		countcopy = CountThreads( &theStuff );
		if ( countcopy > 0 )
			Sleep( 50 );
	}
	while( countcopy != 0 );
	DeleteCriticalSection( &g_bufferSection );

	ShutAndCleanSocket( myServerSocket );
	ShutAndCleanSocket( acceptSocket );		// move to threads
	CleanWSA();

	MutexRelease( theStuff.runMutex );
	CloseHandle( theStuff.runMutex );

	cout << "SampleServer is done." << endl;
	return 0;
}

// Calls ReleaseMutex and shows msg on error
void MutexRelease( HANDLE m )
{
	if ( ReleaseMutex( m ) == false )
	{
		cout << "Mutex did not release" << endl;
	}
}

// TalkOnTheSocket
//	The routine for the talking threads.
//	Receives and sends back a message from a client
//	then shuts down gracefully
void TalkOnTheSocket( talkthreadstuff * threadparams )
{
	// Loop
	// Need to move into a new thread to recv and send and close the 
	//	new socket.
	//	Need a buffer here.		// Make the buffer a shared buffer from main.
	//char buff[MSG_BUFFER_SIZE];
	long errcode = SOCKET_ERROR;
	long othererrcode = SOCKET_ERROR;
	SOCKET acceptSocket = threadparams->talkSocket;
	LONGLONG id = 0;

// TODO: Better to do with a interlockedadd
	InterlockedOr64(&id, threadparams->numThreads );
	do
	{
	//recv msg into msg buffer
		//errcode = recv( acceptSocket, buff, MSG_BUFFER_SIZE-1, 0);
		EnterCriticalSection( &g_bufferSection );
			errcode = recv( acceptSocket, threadparams->msgBuff, MSG_BUFFER_SIZE-1, 0 );
		if ( errcode > 0 )
		{
			threadparams->msgBuff[errcode] = '\0';
			cout << id << " recv: " << threadparams->msgBuff << endl;
			othererrcode = send( acceptSocket, threadparams->msgBuff, errcode, 0 );
		LeaveCriticalSection( &g_bufferSection );
			if( SOCKET_ERROR == othererrcode )
			{
				cout << "sendback " << endl;
				WSAErrorsToConsole::ShowSendError( WSAGetLastError() );
				ShutAndCleanSocket( acceptSocket );
				DecrementThreads( threadparams );
				_endthread();
				return; // not reached //
			}
			cout << "and send " << othererrcode << " bytes." << endl;
		}
		else if ( errcode == 0 )
		{
		LeaveCriticalSection( &g_bufferSection );
			cout << "Time to close." << endl;
		}
		else
		{
		LeaveCriticalSection( &g_bufferSection );
			cout << "recv " << endl;
			WSAErrorsToConsole::ShowRecvError( WSAGetLastError() );
			ShutAndCleanSocket( acceptSocket );
			DecrementThreads( threadparams );
			_endthread();
			return; // not reached //
		}
	//print msg
	}
	while ( errcode > 0 );
	// Exit
	//	Release Winsock 2.2
	errcode = shutdown( acceptSocket, SD_SEND );
	if ( SOCKET_ERROR == errcode )
	{
		WSAErrorsToConsole::ShowShutdownError( WSAGetLastError() );
		ShutAndCleanSocket( acceptSocket );
		DecrementThreads( threadparams );
		_endthread();
		return; // not reached //
	}

	// Need to empty the socket of incoming messages here.

	ShutAndCleanSocket( acceptSocket );

	Sleep(500);  // To see threads queue up.

	DecrementThreads( threadparams );
	_endthread();
	return; // not reached //
}

// Calls shutdown( s, SD_BOTH ), closesocket( s ), and shows errors
void ShutAndCleanSocket( SOCKET s )
{
	long errcode = 0;
	errcode = shutdown( s, SD_BOTH );
	if ( SOCKET_ERROR == errcode )
	{
		WSAErrorsToConsole::ShowShutdownError( errcode );
	}

	errcode = closesocket( s );
	if ( SOCKET_ERROR == errcode )
	{
		WSAErrorsToConsole::ShowCloseSocketError( errcode );
	}
}

// Calls WSACleanUp and shows errors
void CleanWSA()
{
	long errcode = 0;

	errcode = WSACleanup();
	if ( errcode == SOCKET_ERROR )
	{
		WSAErrorsToConsole::ShowWSACleanupError( WSAGetLastError() );
	}
}

// Increments number of threads in the passed param structure
//	Uses the interlockedIncrement64 call to sync with other threads
void IncrementThreads( talkthreadstuff * a_threadstuff )
{
	InterlockedIncrement64( &(a_threadstuff->numThreads) );
}

// Decrements number of threads in the passed param structure
//	Uses the interlockedDecrement64 call to sync with other threads
void DecrementThreads( talkthreadstuff * a_threadstuff )
{
	InterlockedDecrement64( &(a_threadstuff->numThreads) );
}

// Counts number of threads in the passed param structure
//	Uses the interlockedOr64 call to sync with other threads
LONGLONG CountThreads( talkthreadstuff * a_threadstuff )
{
	LONGLONG tempcounts = 0;
	
	InterlockedOr64( &tempcounts, a_threadstuff->numThreads );

	return tempcounts;
}

